﻿/* 程序：被动式无人机检测数据分析类
 * 版本：1.01
 * 说明：基于RT5350的WiFi数据包抓取和MAC地址，通过MAC地址段匹配检测无人机
 * 网站：https://gitee.com/violick/WuRenJiJianCe.git
 * 作者：闵行中学 高二（5）班 谈知行
 * 修改时间：2017.12.01
*/
 
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using UltraSystemLib; 

namespace WiFiScan
{
    struct WiFiFrame
    {
        public byte RxFrameType { set; get; }
        public byte RxSubFrameType { set; get; }
        public byte IsFromAp { set; get; }
        public byte IsFromSta { set; get; }
        public UInt64 Mac1 { set; get; }
        public UInt64 Mac2 { set; get; }
        public UInt64 Mac3 { set; get; }
        public int SignalStrength{ set; get; }
    }
    public delegate void CmdReceivedHandler(UInt32 cmd, object[] paraments);
    class CmdParser
    {
        public CmdReceivedHandler cmdReceivedHandler;
        private ASCIIEncoding encoding = new ASCIIEncoding();
        public byte[] ScanAP()
        {
            return encoding.GetBytes("iwpriv ra0 set SiteSurvey=1\x0d\x0a");
        }
        public byte[] GetAPState()
        {
            return encoding.GetBytes("iwpriv ra0 get_site_survey\x0d\x0a");
        }
        public byte[] SetChannel(int channelNumber)
        {
            return encoding.GetBytes("iwpriv ra0 set Channel=" + channelNumber.ToString() + "\x0d\x0a");
        }
        private char[] cmdBuffer = new char[4096];
        private char[] dataBuffer = new char[4096];
        private int cmdBufferCount = 0;
        private int dataBufferCount = 0;
        private int startCodeCount = 0;
        private bool dataModeFlag = false;
        public void cmdDataIn(byte[] cmdData)
        {
            var cmdChars = new char[cmdData.Length];
            encoding.GetDecoder().GetChars(cmdData, 0, cmdData.Length, cmdChars, 0);
            for (int i = 0; i < cmdChars.Length; i++)
            {
                if (cmdChars[i] == 0)
                {
                    continue;
                }
                if (dataModeFlag)
                {
                    dataBuffer[dataBufferCount++] = cmdChars[i];
                    if (dataBufferCount >= dataBuffer.Length)
                    {
                        dataBufferCount = 0;
                        dataModeFlag = false;
                        continue;
                    }
                }
                else
                {
                    cmdBuffer[cmdBufferCount++] = cmdChars[i];
                    if (cmdBufferCount >= cmdBuffer.Length)
                    {
                        startCodeCount = cmdBufferCount = 0;
                        continue;
                    }
                    if (cmdChars[i] == 'F')
                    {
                        if (startCodeCount++ == 3)
                        {
                            dataModeFlag = true;
                            dataBuffer[0] = dataBuffer[1] = dataBuffer[2] = dataBuffer[3] = 'F';
                            dataBufferCount = startCodeCount;
                            cmdBufferCount -= startCodeCount;
                            startCodeCount = 0;
                            continue;
                        }
                    }
                    else
                    {
                        startCodeCount = 0;
                    }
                }
                if (cmdChars[i] == '\n')
                {
                    if (cmdReceivedHandler != null)
                    {
                        if (dataModeFlag)
                        {
                            var dataString = new string(dataBuffer, 0, dataBufferCount);
                            var frameData = DecodeWifiFrame(dataString);
                            cmdReceivedHandler(0, new object[] { frameData, dataString });
                            dataModeFlag = false;
                        }
                        else
                        {
                            cmdReceivedHandler(255, new object[] { new string(cmdBuffer, 0, cmdBufferCount) });
                            cmdBufferCount = 0;
                        }
                    }
                }
            }
        }
        private WiFiFrame DecodeWifiFrame(string frame)
        {
            var startOffset = 4;
            return new WiFiFrame
            {
                RxFrameType = byte.Parse(frame.Substring(startOffset, 2), NumberStyles.HexNumber),
                RxSubFrameType = byte.Parse(frame.Substring(startOffset + 2, 2), NumberStyles.HexNumber),
                IsFromAp = byte.Parse(frame.Substring(startOffset + 4, 2), NumberStyles.HexNumber),
                IsFromSta = byte.Parse(frame.Substring(startOffset + 6, 2), NumberStyles.HexNumber),
                Mac1 = UInt64.Parse(frame.Substring(startOffset + 8, 12), NumberStyles.HexNumber),
                Mac2 = UInt64.Parse(frame.Substring(startOffset + 20, 12), NumberStyles.HexNumber),
                Mac3 = UInt64.Parse(frame.Substring(startOffset + 32, 12), NumberStyles.HexNumber),
                SignalStrength = int.Parse(frame.Substring(startOffset + 44, 8), NumberStyles.HexNumber)
            };
        }
    }

    enum DeviceType
    {
        Console, Station
    }
    enum UavState
    {
        Online, Offline
    }
    class Uav
    {
        public bool NewTargetFlag { set; get; }
        public DeviceType Type { set; get; }
        public UInt64 Mac { set; get; }
        public UInt64 PeerMac { set; get; }
        public int Channel { set; get; }
        public int Number { set; get; }
        public UavState State { set; get; }
        public DateTime DetectedTime { set; get; }
        public string Manufacturer { set; get; }
        public int SignalStrenth { set; get; }
    }
    class UavCapture
    {
        private CmdParser cmdParser = new CmdParser();     // 帧数据分析类
        private Dictionary<UInt32, int> macList = new Dictionary<UInt32, int>();
        private List<string> manufacturerList = new List<string>();
        public Dictionary<UInt64, Uav> Uavs = new Dictionary<UInt64, Uav>();
        private int groupNumber;
        public bool DispalyAll { set; get; }
        public int ChannelSelected { set; get; }
        public UavCapture()
        {
            MsgReceived = new ConcurrentQueue<string>();
            LoadMacManufacturerList();  // 加载MAC-制造商对应表
            cmdParser.cmdReceivedHandler = OnDataReceived;   // 设置串口接收数据处理方法入口
        }
        public ConcurrentQueue<string> MsgReceived { set; get; }
        private void LoadMacManufacturerList()
        {
            try
            {
                using (var mapList = new UltraXMLDocument(@".\", "mac_map.xml"))
                {
                    foreach (var item in mapList.RootElement.Element("MAC_LIST").Elements())
                    {
                        var macIndex = item.Value.ToString();
                        macList.Add(UInt32.Parse(macIndex.Substring(0, 6), NumberStyles.HexNumber), int.Parse(macIndex.Substring(7)));
                    }
                    foreach (var item in mapList.RootElement.Element("Manufacturers").Elements())
                    {
                        var macIndex = item.Value.ToString();
                        manufacturerList.Add(macIndex);
                    }
                }
            }
            catch (Exception)
            {
                MessageBox.Show("错误：打开MAC映射文件\"mac_map.xml\"失败。");
            }

        }
        private void GenerateMacList()
        {
            using (StreamReader sr = new StreamReader(@"G:\技术资料\AP\Apps\mac_list.txt"))
            {
                var manufacturerList = new Dictionary<string, int>();
                var macs = new List<string>();
                string line;
                int manuCount = 0;
                var xmlManuList = new UltraXMLDocument();
                xmlManuList.Create("mac_map.xml", "MAC to Manufacturer map");
                xmlManuList.RootElement.Add(new XElement("MAC_LIST"));
                var macList = xmlManuList.RootElement.Element("MAC_LIST");
                while ((line = sr.ReadLine()) != null)
                {
                    var offset = line.IndexOf('\t');
                    if (offset != 6)
                    {
                        continue;
                    }
                    var mac = line.Substring(0, offset);
                    var manufacturer = line.Substring(offset + 1);
                    if (macs.Contains(mac))
                    {
                        continue;
                    }
                    else
                    {
                        macs.Add(mac);
                    }
                    if (!manufacturerList.ContainsKey(manufacturer))
                    {
                        manufacturerList.Add(manufacturer, manuCount++);
                    }
                    xmlManuList.AddItem(macList, "MAC", mac + '-' + manufacturerList[manufacturer]);
                }

                xmlManuList.RootElement.Add(new XElement("Manufacturers"));
                var manufacturers = xmlManuList.RootElement.Element("Manufacturers");

                foreach (var item in manufacturerList)
                {
                    xmlManuList.AddItem(manufacturers, "Vender", item.Key);
                }
                xmlManuList.SaveDocument();
            }
        }
        public UInt32 FramesCount { set; get; }
        public UInt32 UavFramesCount { set; get; }
        public void OnDataReceived(UInt32 id, object[] paraments)
        {
            if (id == 255)  // 模块命令直接显示
            {
                MsgReceived.Enqueue(paraments[0] as string);
                return;
            }
            FramesCount++;
            var frame = (WiFiFrame)paraments[0];
            var frameText = string.Empty;
            string manufacturer = string.Empty;
            if (frame.RxFrameType == 0x00 && frame.RxSubFrameType == 0x08)
            {
                frameText += "控制台信标: MAC=" + frame.Mac2.ToString("X");
                if (GetManufacturer(frame.Mac2, out manufacturer))
                {
                    if (Uavs.ContainsKey(frame.Mac2))
                    {
                        Uavs[frame.Mac2].DetectedTime = DateTime.Now;
                        Uavs[frame.Mac2].State = UavState.Online;
                        Uavs[frame.Mac2].SignalStrenth = frame.SignalStrength;
                    }
                    else
                    {
                        Uavs.Add(frame.Mac2, new Uav()
                        {
                            Mac = frame.Mac2,
                            Type = DeviceType.Console,
                            PeerMac = 0,
                            Channel = ChannelSelected,
                            Number = -1,
                            State = UavState.Online,
                            DetectedTime = DateTime.Now,
                            Manufacturer = manufacturer,
                            NewTargetFlag = true,
                            SignalStrenth = frame.SignalStrength
                        });
                    }
                }
                frameText += ' ' + manufacturer;
            }
            else if (frame.RxFrameType == 0x00 && frame.RxSubFrameType == 0x04)
            {
                var consoleMatchedFlag = false;
                frameText += "探测请求：FromConsole:" + frame.IsFromAp + " FromStation:" + frame.IsFromSta
                    + ' ' + "Console MAC:";
                frameText += frame.Mac1.ToString("X");
                if (GetManufacturer(frame.Mac1, out manufacturer))
                {
                    consoleMatchedFlag = true;
                    if (Uavs.ContainsKey(frame.Mac1))
                    {
                        Uavs[frame.Mac1].DetectedTime = DateTime.Now;
                        Uavs[frame.Mac1].State = UavState.Online;
                        Uavs[frame.Mac1].SignalStrenth = frame.SignalStrength;
                    }
                    else
                    {
                        Uavs.Add(frame.Mac1, new Uav()
                        {
                            Mac = frame.Mac1,
                            Type = DeviceType.Console,
                            PeerMac = 0,
                            Channel = ChannelSelected,
                            Number = -1,
                            State = UavState.Online,
                            DetectedTime = DateTime.Now,
                            Manufacturer = manufacturer,
                            NewTargetFlag = true,
                            SignalStrenth = frame.SignalStrength
                        });
                    }
                }
                frameText += ' ' + manufacturer;

                frameText += " Station MAC:";
                frameText += frame.Mac2.ToString("X");
                if (GetManufacturer(frame.Mac2, out manufacturer))
                {
                    if (Uavs.ContainsKey(frame.Mac2))
                    {
                        Uavs[frame.Mac2].DetectedTime = DateTime.Now;
                        Uavs[frame.Mac2].State = UavState.Online;
                        Uavs[frame.Mac2].SignalStrenth = frame.SignalStrength;
                    }
                    else
                    {
                        Uavs.Add(frame.Mac2, new Uav()
                        {
                            Mac = frame.Mac2,
                            Type = DeviceType.Station,
                            PeerMac = 0,
                            Channel = ChannelSelected,
                            Number = -1,
                            State = UavState.Online,
                            DetectedTime = DateTime.Now,
                            Manufacturer = manufacturer,
                            NewTargetFlag = true,
                            SignalStrenth = frame.SignalStrength
                        });
                    }
                    if (consoleMatchedFlag)
                    {
                        Uavs[frame.Mac1].PeerMac = frame.Mac2;
                        Uavs[frame.Mac2].PeerMac = frame.Mac1;
                        Uavs[frame.Mac1].Number = Uavs[frame.Mac2].Number = groupNumber++;
                    }
                }
                frameText += ' ' + manufacturer;
            }
            else if (frame.RxFrameType == 0x01 && frame.RxSubFrameType == 0x0d)
            {
                frameText += "确认应答： FromConsole:" + frame.IsFromAp + " FromStation:" + frame.IsFromSta
                    + ' ' + "MAC:";
                frameText += frame.Mac1.ToString("X");
                if (GetManufacturer(frame.Mac1, out manufacturer))
                {
                    if (Uavs.ContainsKey(frame.Mac1))
                    {
                        Uavs[frame.Mac1].DetectedTime = DateTime.Now;
                        Uavs[frame.Mac1].State = UavState.Online;
                        Uavs[frame.Mac1].SignalStrenth = frame.SignalStrength;
                    }
                    else
                    {
                        Uavs.Add(frame.Mac1, new Uav()
                        {
                            Mac = frame.Mac1,
                            Type = DeviceType.Station,
                            PeerMac = 0,
                            Channel = ChannelSelected,
                            Number = -1,
                            State = UavState.Online,
                            DetectedTime = DateTime.Now,
                            Manufacturer = manufacturer,
                            NewTargetFlag = true,
                            SignalStrenth = frame.SignalStrength
                        });
                    }
                }
                frameText += ' ' + manufacturer;
            }
            else
            {
                return;
            }
            frameText += " STR:" + frame.SignalStrength + "dB" + Environment.NewLine;
            if (DispalyAll)
            {
                MsgReceived.Enqueue(frameText);
            }
        }
        public bool GetManufacturer(UInt64 mac, out string manufacturer)
        {
            try
            {
                var fact = (UInt32)(mac >> 24);
                var index = macList[fact];
                manufacturer = manufacturerList[index]; 
                if (manufacturer.Equals("SZ DJI TECHNOLOGY CO.,LTD") || 
                    //manufacturer.Equals("TP-LINK TECHNOLOGIES CO.,LTD.") || 
                    manufacturer.Equals("Xiaomi Communications Co Ltd"))
                {
                    UavFramesCount++;
                    return true;
                }
                return false;
            }
            catch (Exception)
            {
                manufacturer = "未知地址";
                return false;
            }
        }
        public void OnComDataIn(byte[] comData)
        {
            cmdParser.cmdDataIn(comData);            
        }
        public void Test()
        {
            using (var textReader = new StreamReader(@".\debug1.txt"))
            {
                try
                {
                    while (true)
                    {
                        var line = textReader.ReadLine();
                        if (line.Equals(string.Empty))
                        {
                            continue;
                        }
                        line += "\x0d\x0a";
                        cmdParser.cmdDataIn(Encoding.ASCII.GetBytes(line));
                    }
                }
                catch (Exception)
                {
                }
            }
        }
        public byte[] SetChannel(int channel)
        {
            return cmdParser.SetChannel(channel);
        }
        public byte[] ScanAP()
        {
            return cmdParser.ScanAP();
        }
        public byte[] GetAPState()
        {
            return cmdParser.GetAPState();
        }
    }
}
